/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package checkers.businessLogic.game;

import java.util.Timer;
import java.util.TimerTask;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author artinshahverdian
 */
public class Game {

    private Board board;
    private Player player1, player2;
    private String name;
    private Piece.Color turn;
    private boolean gameFinished;
    private Player winner = null;
    private Timestamp creationDate;
    private Timestamp terminationDate;
    private long TIMER_VALUE = 60000;
    transient Timestamp lastMoveTime;
    transient private Timer timer = new Timer();
    transient private TimerTask timerTask;
    transient private GameListener listener;

    public Game(String gameTableName, String playerName, String playerEmail, int rating, String userId) {
        board = new Board();

        player1 = new Player(playerName, playerEmail, rating, userId, Piece.Color.WHITE);

        this.name = gameTableName;
        turn = turn.WHITE;
        gameFinished = false;
        creationDate = new Timestamp(Calendar.getInstance().getTimeInMillis());



        init();
    }

    private void init() {

        Field[][] field = getBoard().getFields();
        for (int i = 0; i < 8; i++)
            for (int j = 0; j < 8; j++)
                field[i][j] = new Field();

        // placing the WHITE pieces in their initial position

        field[0][0].setPiece(new Piece(Piece.Color.WHITE, Piece.Type.PIECE));
        field[0][2].setPiece(new Piece(Piece.Color.WHITE, Piece.Type.PIECE));
        field[0][4].setPiece(new Piece(Piece.Color.WHITE, Piece.Type.PIECE));
        field[0][6].setPiece(new Piece(Piece.Color.WHITE, Piece.Type.PIECE));


        field[1][1].setPiece(new Piece(Piece.Color.WHITE, Piece.Type.PIECE));
        field[1][3].setPiece(new Piece(Piece.Color.WHITE, Piece.Type.PIECE));
        field[1][5].setPiece(new Piece(Piece.Color.WHITE, Piece.Type.PIECE));
        field[1][7].setPiece(new Piece(Piece.Color.WHITE, Piece.Type.PIECE));

        field[2][0].setPiece(new Piece(Piece.Color.WHITE, Piece.Type.PIECE));
        field[2][2].setPiece(new Piece(Piece.Color.WHITE, Piece.Type.PIECE));
        field[2][4].setPiece(new Piece(Piece.Color.WHITE, Piece.Type.PIECE));
        field[2][6].setPiece(new Piece(Piece.Color.WHITE, Piece.Type.PIECE));

        // placing the BLACK pieces in their initial position

        field[7][1].setPiece(new Piece(Piece.Color.BLACK, Piece.Type.PIECE));
        field[7][3].setPiece(new Piece(Piece.Color.BLACK, Piece.Type.PIECE));
        field[7][5].setPiece(new Piece(Piece.Color.BLACK, Piece.Type.PIECE));
        field[7][7].setPiece(new Piece(Piece.Color.BLACK, Piece.Type.PIECE));


        field[6][0].setPiece(new Piece(Piece.Color.BLACK, Piece.Type.PIECE));
        field[6][2].setPiece(new Piece(Piece.Color.BLACK, Piece.Type.PIECE));
        field[6][4].setPiece(new Piece(Piece.Color.BLACK, Piece.Type.PIECE));
        field[6][6].setPiece(new Piece(Piece.Color.BLACK, Piece.Type.PIECE));

        field[5][1].setPiece(new Piece(Piece.Color.BLACK, Piece.Type.PIECE));
        field[5][3].setPiece(new Piece(Piece.Color.BLACK, Piece.Type.PIECE));
        field[5][5].setPiece(new Piece(Piece.Color.BLACK, Piece.Type.PIECE));
        field[5][7].setPiece(new Piece(Piece.Color.BLACK, Piece.Type.PIECE));

    }

    public boolean joinPlayer(String playerName, String playerEmail, int rating, String userId) {
        if (getPlayer2() == null) {

            setPlayer2(new Player(playerName, playerEmail, rating, userId, Piece.Color.BLACK));
            setTimerTask(new MyTimerTask());
            getTimer().schedule(getTimerTask(), getTIMER_VALUE());
            lastMoveTime = new Timestamp(System.currentTimeMillis());
            return true;


        }

        return false;


    }

    /**
     * @return the board
     */
    public Board getBoard() {
        return board;
    }

    /**
     * @param board the board to set
     */
    public void setBoard(Board board) {
        this.board = board;
    }

    /**
     * @return the player1
     */
    public Player getPlayer1() {
        return player1;
    }

    /**
     * @param player1 the player1 to set
     */
    public void setPlayer1(Player player1) {
        this.player1 = player1;
    }

    /**
     * @return the player2
     */
    public Player getPlayer2() {
        return player2;
    }

    /**
     * @return the name
     */
    public String getName() {
        return name;
    }

    /**
     * @param name the name to set
     */
    public void setName(String name) {
        this.name = name;
    }

    public boolean move(Position from, Position to) throws SQLException, Exception {
        if (isGameFinished() != true) {
            if (getTurn() == getBoard().getField(from).getPiece().getColor()) {

                boolean isMovePossible = isMovePossible(from, to, getBoard().getPieceAtPosition(from));

                if (isMovePossible) {
                    getTimerTask().cancel();
                    setTimerTask(new MyTimerTask());
                    getTimer().schedule(getTimerTask(), getTIMER_VALUE());

                    makeMove(from, to);

                    lastMoveTime = new Timestamp(System.currentTimeMillis());

                    if (getTurn() == getTurn().WHITE)
                        setTurn(getTurn().BLACK);
                    else
                        setTurn(getTurn().WHITE);
                    Player p = checkForWinner();
                    if (p != null) {

                        if (p.equals(getPlayer1()))
                            setWinner(getPlayer1());
                        else
                            setWinner(getPlayer2());

                        fireGameFinishedEvent();
                    }

                    return true;

                }

            }
        }
        return false;

    }

    private Player checkForWinner() {

        int player1PieceCount = 0;
        int player2PieceCount = 0;
        for (int row = 0; row < 8; row++)
            for (int column = 0; column < 8; column++) {
                Piece piece = getBoard().getField(new Position(row, column)).getPiece();
                if (piece != null) {
                    if (piece.getColor() == Piece.Color.WHITE)
                        player1PieceCount++;
                    else
                        player2PieceCount++;
                }

            }
        if (player1PieceCount == 0)
            return getPlayer2();
        else if (player2PieceCount == 0)
            return getPlayer1();
        return null;


    }

    private boolean isMovePossible(Position from, Position to, Piece piece) {

        // checking the possibility of move for an ORDINARY piece

        Field[][] field = getBoard().getFields();

        if (piece.getType() == Piece.Type.PIECE) {


            if (Math.abs(to.column - from.column) == 1 && !field[to.row][to.column].isBusy()) {
                if (piece.getColor() == Piece.Color.WHITE) {
                    if (to.row - from.row == 1)
                        return true;
                }
                else if (piece.getColor() == Piece.Color.BLACK) {
                    if (to.row - from.row == -1)
                        return true;

                }



            }
            else if (Math.abs(to.row - from.row) == 2 && !field[to.row][to.column].isBusy()) {

                if (piece.getColor() == Piece.Color.WHITE) {
                    if (to.column - from.column == 2) {
                        if (field[from.row + 1][from.column + 1].getPiece().getColor() != Piece.Color.WHITE)
                            return true;

                    }
                    else if (to.column - from.column == -2)
                        if (field[from.row + 1][from.column - 1].getPiece().getColor() != Piece.Color.WHITE)
                            return true;
                }
                else if (piece.getColor() == Piece.Color.BLACK) {

                    if (to.column - from.column == 2) {
                        if (field[from.row - 1][from.column + 1].getPiece().getColor() != Piece.Color.BLACK)
                            return true;

                    }
                    else if (to.column - from.column == -2)
                        if (field[from.row - 1][from.column - 1].getPiece().getColor() != Piece.Color.BLACK)
                            return true;
                }
            }


        }


        // checking the possibility of move for a KING piece

        if (piece.getType() == Piece.Type.KING) {
            if (piece.getColor() == Piece.Color.WHITE) {
                int i = from.column, j = from.row, k = 0;   //k is the count of the opponents pieces on the move path
                boolean result = false;

                for (; i <= to.column; j++) {
                    if (field[i][j].isBusy()) {
                        if (field[i][j].getPiece().getColor() == piece.getColor()) {
                            result = false;
                            break;
                        }
                        else {
                            k++;
                        }
                    }
                    if (from.column < to.column)
                        i++;
                    else
                        i--;
                }
                if (result == true && k <= 1) {
                    return true;
                }
            }
            else if (piece.getColor() == Piece.Color.BLACK) {
                int i = from.column, j = from.row, k = 0;   //k is the count of the opponents pieces on the move path
                boolean result = false;

                for (; i <= to.column; j--) {
                    if (field[i][j].isBusy()) {
                        if (field[i][j].getPiece().getColor() == piece.getColor()) {
                            result = false;
                            break;
                        }
                        else {
                            k++;
                        }
                    }
                }
                if (result == true && k <= 1) {
                    return true;
                }

            }

        }


        return false;



    }

    private void makeMove(Position from, Position to) {   //removes pieces in the way after making a capture move

        Field[][] field = getBoard().getFields();
        Piece p = field[from.row][from.column].getPiece();
        int i = from.row, j = from.column;
        if (p.getColor() == Piece.Color.WHITE) {
            for (; i <= to.row; i++) {

                if (field[i][j].isBusy())
                    field[i][j].removePiece();


                if (from.column < to.column)
                    j++;
                else
                    j--;


            }

        }


        if (p.getColor() == Piece.Color.BLACK) {
            for (; i >= to.row; i--) {

                if (field[i][j].isBusy())
                    field[i][j].removePiece();


                if (from.column < to.column)
                    j++;
                else
                    j--;


            }

        }

        field[to.row][to.column].setPiece(p);

    }

    public long getRemainingTime() {

        Timestamp now = new Timestamp(System.currentTimeMillis());

        long diff = Math.abs(now.getTime() - lastMoveTime.getTime());

        long res = TIMER_VALUE - diff;

        return res;
    }

    /**
     * @return the gameFinished
     */
    public boolean isGameFinished() {
        return gameFinished;
    }

    /**
     * @param gameFinished the gameFinished to set
     */
    public void setGameFinished(boolean gameFinished) {
        this.gameFinished = gameFinished;
    }

    /**
     * @return the winner
     */
    public Player getWinner() {
        return winner;
    }

    /**
     * @param winner the winner to set
     */
    public void setWinner(Player winner) {
        this.winner = winner;
    }

    /**
     * @param player2 the player2 to set
     */
    public void setPlayer2(Player player2) {
        this.player2 = player2;
    }

    /**
     * @return the turn
     */
    public Piece.Color getTurn() {
        return turn;
    }

    /**
     * @param turn the turn to set
     */
    public void setTurn(Piece.Color turn) {
        this.turn = turn;
    }

    /**
     * @return the creationDate
     */
    public Timestamp getCreationDate() {
        return creationDate;
    }

    /**
     * @param creationDate the creationDate to set
     */
    public void setCreationDate(Timestamp creationDate) {
        this.creationDate = creationDate;
    }

    /**
     * @return the terminationDate
     */
    public Timestamp getTerminationDate() {
        return terminationDate;
    }

    /**
     * @param terminationDate the terminationDate to set
     */
    public void setTerminationDate(Timestamp terminationDate) {
        this.terminationDate = terminationDate;
    }

    /**
     * @return the TIMER_VALUE
     */
    public long getTIMER_VALUE() {
        return TIMER_VALUE;
    }

    /**
     * @param TIMER_VALUE the TIMER_VALUE to set
     */
    public void setTIMER_VALUE(long TIMER_VALUE) {
        this.TIMER_VALUE = TIMER_VALUE;
    }

    /**
     * @return the timer
     */
    public Timer getTimer() {
        return timer;
    }

    /**
     * @param timer the timer to set
     */
    public void setTimer(Timer timer) {
        this.timer = timer;
    }

    /**
     * @return the timerTask
     */
    public TimerTask getTimerTask() {
        return timerTask;
    }

    /**
     * @param timerTask the timerTask to set
     */
    public void setTimerTask(TimerTask timerTask) {
        this.timerTask = timerTask;
    }

    /**
     * @return the listener
     */
    public GameListener getListener() {
        return listener;
    }

    /**
     * @param listener the listener to set
     */
    public void setListener(GameListener listener) {
        this.listener = listener;
    }

    public void fireGameFinishedEvent() throws SQLException, Exception {
        gameFinished = true;
        terminationDate = new Timestamp(Calendar.getInstance().getTimeInMillis());
        listener.onGameFinished(this);
    }

    ////////--------------------Timer Task Class -------------------------------
    class MyTimerTask extends TimerTask {

        @Override
        public void run() {
            if (getTurn() == getTurn().WHITE) {
                setWinner(getPlayer2());

            }
            else
                setWinner(getPlayer1());
            getTimer().cancel();

            try {
                fireGameFinishedEvent();
            }
            catch (Exception ex) {
                Logger.getLogger(Game.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
}
